# @Time : 2025-01-02 13:52
# @Author : Fioman 
# @Phone : 13149920693
"""
mysql操作的单例类,和mysql的操作都放在这个类里面.
这个类在创建的时候,会开启多个线程,这些线程负责数据库的监听
"""
import threading
import time

import app_config
from common.exception_helper import error_handler_eap
from db_tool.db_config import TableName
from db_tool.db_handler import DbHandler
from eap_tool import table_access
from eap_tool import table_update
from eap_tool.eap_logger import debug_eap
from personal_tool.data_result import DataResult
from personal_tool.enum_data_eap import PcSignalType, EapMsgType, OperationMode
from web_tool.send_to_web_handler import eap_data_update_to_web, show_notification_in_web

db = DbHandler()


class EapHandler:
    """
    mysql对外开发的操作类,UI和程序的内部调用
    """
    _instance = None
    _initFlag = False
    _lock = threading.Lock()

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            with cls._lock:
                cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        if not self.__class__._initFlag:
            debug_eap("in MysqlHandler().__init__() 调用,进行初始化")
            # 创建所有的表,如果表不存在的话
            table_access.create_total_tables()
            self.operationMode = ""  # 当前的操作模式,方便第一次加载参数的时候对比
            # 加载EapParams
            self.reload_eap_params()
            self.__class__._initFlag = True

    def pc_data_craft_listener(self):
        """
        pc_data_craft 工艺质量表的监控线程
        :return:
        """
        while app_config.isMainAlive:
            if self.operationMode == OperationMode.Eap模式.name:
                table_update.pc_data_craft_update(self.deleteDataAuto)
                # 更新到web
                eap_data_update_to_web(EapMsgType.PcDataCraft)
            time.sleep(self.pcDataCraftDuration)

    def pc_data_status_listener(self):
        """
        pc_data_status 不实时采集设备状态表
        :return:
        """
        while app_config.isMainAlive:
            if self.operationMode == OperationMode.Eap模式.name:
                table_update.pc_data_status_update(self.deleteDataAuto)
                # 更新到web
                eap_data_update_to_web(EapMsgType.PcDataStatus)
            time.sleep(self.pcDataStatusDuration)

    def eap_signal_status_listener(self):
        """
        eap_signal_status 表的监控获取eap信号
        :return:
        """
        while app_config.isMainAlive:
            if self.operationMode == OperationMode.Eap模式.name:
                resList = table_access.eap_signal_status_fetch_all()
                if len(resList) != 0:
                    self.deal_eap_siginal_status(resList)
                eap_data_update_to_web(EapMsgType.EapSignalStatus)
            time.sleep(self.eapSignalStatusDuration)

    def start_eap_tasks(self):
        """
        开启Eap的线程任务
        :return:
        """
        self.pcDataCraftThread = threading.Thread(target=self.pc_data_craft_listener,
                                                  name="pcDataCraftThread", daemon=True)
        self.pcDataCraftThread.start()

        self.pcDataStatusThread = threading.Thread(target=self.pc_data_status_listener,
                                                   name="pcDataCountT", daemon=True)
        self.pcDataStatusThread.start()

        self.eapSignalStatusThread = threading.Thread(target=self.eap_signal_status_listener,
                                                      name="eapSignalStatusT", daemon=True)
        self.eapSignalStatusThread.start()

    def reload_eap_params(self):
        """
        加载Eap参数
        :return:
        """
        try:
            result = db.get_data_one(TableName.eapParams)
            if not result.state:
                debug_eap(result.info)
                return
            data = result.data
            self.pcDataCraftDuration = data.get("pcDataCraft")
            self.pcDataStatusDuration = data.get("pcDataStatus")
            self.eapSignalStatusDuration = data.get("eapSignalStatus")
            self.deleteDataAuto = True if data.get("deleteDataAuto") == "是" else False
            operationMode = data.get("operationMode")
            if self.__class__._initFlag:
                if operationMode != self.operationMode:
                    table_update.pc_signal_status_insert_one(PcSignalType.AUTOMANUAL,
                                                             OperationMode(operationMode).value)
            self.operationMode = operationMode

            self.operationMode = operationMode
            debug_eap(f"eap参数加载,表轮询间隔: pc_data_craft: {self.pcDataCraftDuration},"
                      f"pc_data_status: {self.pcDataStatusDuration},"
                      f"eap_signal_status: {self.eapSignalStatusDuration},"
                      f"operationMode: {self.operationMode},deleteDataAuto: {self.deleteDataAuto}")

        except Exception as e:
            errInfo = f"in  MysqlHandler().reload_eap_params() error: {e}"
            debug_eap(errInfo)

    @staticmethod
    def get_eap_params_from_web():
        """
        web请求eapParams表的数据,这种请求的返回方式是DataResult的形式
        :return:
        """
        return db.get_data_list(TableName.eapParams)

    def save_eap_params_from_web(self, newData) -> DataResult:
        """
        web请求保存eapParams表的数据,返回方式DataResult
        :return:
        """
        query = {
            "key": newData["key"],
        }
        result = db.update_data(TableName.eapParams, query, newData)
        if result.state:
            self.reload_eap_params()
        return result

    @staticmethod
    def get_size_layouts_from_web() -> DataResult:
        """
        web 请求 sizeLayouts表的值,这个表是本地数据库表的内容,并且这里后台不会修改,
        只能通过界面去修改更新,所以这里采用直接返回数据,更新数据的方式
        @return:
        """
        return db.get_data_list(TableName.sizeLayouts)

    @staticmethod
    def save_size_layouts_from_web(newData) -> DataResult:
        """
        界面上点击保存sizeLayouts表数据,根据key去保存,有两种情况一种是添加数据的保存,
        一种是更新数据的保存,所以要先看看数据是否存在,依据就是key
        @param newData:
        @return:
        """
        query = {
            "key": newData["key"],
        }
        result = db.get_data_one(TableName.sizeLayouts, query)
        if result.state:
            # 如果数据已经存在就去更新
            return db.update_data(TableName.sizeLayouts, query, newData)
        else:
            # 如果不存在就插入
            return db.insert_data(TableName.sizeLayouts, newData)

    @staticmethod
    def delete_size_layouts_from_web(key) -> DataResult:
        """
        界面上点击删除某一条数据
        @param key:
        @return:
        """
        query = {
            "key": key
        }
        return db.delete_data(TableName.sizeLayouts, query)

    @staticmethod
    def get_pc_data_craft_from_web() -> DataResult:
        """
        web请求获取pc_data_craft的值,一律通过update_to_web的方式去更新这个值,这里不出现异常就返回ok
        @return:
        """
        return eap_data_update_to_web(EapMsgType.PcDataCraft)

    @staticmethod
    def get_pc_data_manual_from_web() -> DataResult:
        """
        web请求获取 pc_data_manualmaterial 的值,一律通过update_to_web的方式去更新这个值,
        这里不出现异常就返回ok
        @return:
        """
        return eap_data_update_to_web(EapMsgType.PcDataManual)

    @staticmethod
    def get_pc_data_alarm_from_web() -> DataResult:
        """
         web请求获取 pc_data_alarm 的值,一律通过update_to_web的方式去更新这个值,
        这里不出现异常就返回ok
        @return:
        """
        return eap_data_update_to_web(EapMsgType.PcDataAlarm)

    @staticmethod
    def get_pc_data_emp_from_web() -> DataResult:
        """
         web请求获取 pc_data_emp 的值,一律通过update_to_web的方式去更新这个值,
        这里不出现异常就返回ok
        @return:
        """
        return eap_data_update_to_web(EapMsgType.PcDataEmp)

    @staticmethod
    def get_pc_data_status_from_web() -> DataResult:
        """
        web请求获取 pc_data_status 的值,一律通过update_to_web的方式去更新这个值,
        这里不出现异常就返回ok
        @return:
        """
        return eap_data_update_to_web(EapMsgType.PcDataStatus)

    @staticmethod
    def get_pc_data_count_from_web() -> DataResult:
        """
        web请求获取 pc_data_count 的值,一律通过update_to_web的方式去更新这个值,
        这里不出现异常就返回ok
        @return:
        """
        return eap_data_update_to_web(EapMsgType.PcDataCount)

    @staticmethod
    def get_pc_signal_status_from_web() -> DataResult:
        """
        web请求获取pc_signal_status的值,一律通过update_to_web的方式去更新这个值,
        这里如果不出现异常,就返回ok
        :return:
        """
        return eap_data_update_to_web(EapMsgType.PcSignalStatus)

    @staticmethod
    def get_eap_signal_form_from_web() -> DataResult:
        """
        web请求获取 eap_signal_form 的值,一律通过update_to_web的方式去更新这个值,
        这里不出现异常就返回ok
        @return:
        """
        return eap_data_update_to_web(EapMsgType.EapSignalStatus)

    @error_handler_eap
    def deal_eap_siginal_status(self, eapSignalData):
        """
        处理eap的消息
        :param eapSignalData:
        :return:
        """
        debug_eap(f"EapSignalStatus接收到信号: \n{eapSignalData}")
        for data in eapSignalData:
            signalCode = data["SIGNALCODE"]
            signalValue = data["SIGNALVALUE"]
            if signalCode == "MATERIALDOWN" and signalValue == "1":
                debugInfo = f"接收到Eap信号: Eap通知设备资料下发完毕."
                self.deal_plan_download()  # 加载任务
            elif signalCode == "UPDATEPLAN":
                debugInfo = f"接收到Eap信号: 通知设备计划修改,lot号;lot数量"
                self.deal_update_plan(signalValue)
            elif signalCode == "DELETEPLAN":
                debugInfo = f"接收到Eap信号: 通知设备计划删除"
            elif signalCode == "MATERIALERROR":
                debugInfo = f"接收到Eap信号: 通知设备资料下发异常: {signalValue:}"
            else:
                debugInfo = f"接收到Eap信号: 未定义信号: {signalCode}"
            debug_eap(debugInfo)
            filedName = "SIGNALCODE"
            table_access.eap_signal_status_delete_one(filedName, signalCode)

    @error_handler_eap
    def deal_plan_download(self):
        """
        处理工单切换的任务
        :return:
        """
        # 1. 获取工单资料表的内容
        orderList = table_access.eap_material_data_fetch_all()
        debug_eap(f"读取eap_material_data的数据内容: {orderList}")
        downloadRes = "1"  # 默认加载参数成功返回"1",其他返回"2"
        if len(orderList) >= 3:
            width, height, cropThickness, lotId, recipeId, planCount = (None,) * 6
            for data in orderList:
                lotId = data["CONTAINER"]
                recipeId = data["PRODUCT"]
                planCount = data["COUNT"]
                itemCode = data["ITEMCODE"]
                itemValue = data["ITEMVALUE"]
                if itemCode == "PNL_LENGTH":
                    width = float(itemValue)
                elif itemCode == "PNL_WIDTH":
                    height = float(itemValue)
                elif itemCode == "CropperThickness":
                    cropThickness = float(itemValue)
            if all([width, height, cropThickness, lotId, recipeId, planCount]):
                query = {
                    "width": width,
                    "height": height,
                }
                result = db.get_data_one(TableName.sizeLayouts, query)
                if not result.state:
                    errorInfo = f"Eap工单下发失败: 此工单尺对应的拼板方式和排板方式在本地的配置表中没有配置"
                    debug_eap(errorInfo)
                    show_notification_in_web("Eap工单下发失败通知", errorInfo)
